'''My generic 3D model object that's used as a temporary storage.

The following data structures are defined:

Faces dictionary
Stores the faces that make up a particular mesh and the material used
{meshName : [Face objects]
}

meshes dictionary
Stores mesh related information

{Meshname :
  { "transform" : Transformation matrix (16-float) }
}

vertex dictionary
Stores a list of vertices. vertex number decided by index.

{MeshName : [Vertex objects]
}

dupeVertex dictionary
Any duplicate vertices should be stored here temporarily if necessary. Used
if the UV's are not stored with the vertices.

{meshName: [Vertex objects]
}

material dictionary
stores material information.
{matNum :
  {"name" : matName,
   "texture" : texName,
   "rgba" : [r,g,b,a],
   "specular" : specular,
   "emissive" : emissive,
   "power" : power
  }
}

bones dictionary
stores bone information. Key is the bone name, value is a dictionary
{boneName: {vert1 : weight1, vert2 : weight2}}

frames dictionary
Maps names to indices in the frame list

{ frameName : index
}
'''

from euclid import *
import copy              #for duplicating objects quickly

class Model3D(object):
    '''This object provides a standardized format that parsers will use'''
    
    axis = {"x" : 0, "y" : 1, "z" : 2}
    
    def __init__(self, format3D):
        '''Creates data structures to hold required information'''
        
        self.format3D = format3D
        self._objList = []
        self._meshes = {}
        
        self._matList = []
        self._texList = []
        self._textures = {}
        self._materials = []
        self._bones = {}
        self._boneList = []
        self._frames = []
        self._frameDict = {}
        self._transform = Matrix4()
        
        self.max_x = 0
        self.max_y = 0
        self.max_z = 0
        self.min_x = 0
        self.min_y = 0
        self.min_z = 0
        
    #debug functions
    
    def join_faces(self):
        '''Create arbitrary faces to join all of the vertices together for
        all objects. Hide faces and show only vertices in metasequoia.'''
        
        for objName in self.get_objects():
            vertCount = self.vert_count(objName)
            
            face = []
            faceNum = 0
            for i in xrange(vertCount):
                face.append(i)
                if len(face) == 3:
                    self.create_face(objName, faceNum)
                    self.add_face_verts(objName, faceNum, face)
                    face = []
                    faceNum += 1
            while len(face) != 3:
                face.append(0)
            self.create_face(objName, faceNum)
            self.add_face_verts(objName, faceNum, face)
    
    #transform functions
        
    def set_transform(self, float16):
        '''Takes a 16-float list and set the matrix'''
        
        self._transform[:] = float16[:]
        
    def apply_scale(self, factor):
        
        print "scale"
        self._transform = Matrix4.new_scale(factor, factor, factor)
        for objName in self._objList:
            new_vertices = []
            vertNum = self.vert_count(objName)
            for i in xrange(0, vertNum):
                oldx, oldy, oldz = self.get_coords(objName, i)
                old_point = Point3(oldx, oldy, oldz)
                new_coords = list(self._transform * old_point)
                self._meshes[objName].vertices[i].coords = new_coords
        
    def apply_transform(self, transform):
        
        new_vertices = {}
        objs = self.get_objects()
        for objName in objs:
            new_vertices[objName] = {}
            vertNum = self.vert_count(objName)            
            for i in xrange(0, vertNum):
            
                old_coords = self.get_coords(objName, i)
                new_coords = list(transform * old_coords)
                self._vertices[objName][i]["coords"] = new_coor
        self._vertices = new_vertices
        
    #matrix operations

    def update_max_min(self, coords):
        
        self.max_x = max(self.max_x, coords[0])
        self.min_x = min(self.min_x, coords[0])
        self.max_y = max(self.max_y, coords[1])
        self.min_y = min(self.min_y, coords[1])
        self.max_z = max(self.max_z, coords[2])
        self.min_z = min(self.min_z, coords[2])
        
    def rotate_model(self):
        '''Rotate the model along an axis by the given angle'''
        
        new_vertices = {}
        
        objs = self.get_objects()
        for objName in objs:
            new_vertices[objName] = {}
            vertNum = self.vert_count(objName)
            for i in xrange(0, vertNum):
                old_x, old_y, old_z = self.get_coords(objName, i)
                new_x = old_x
                new_y = old_z * -1.0
                new_z = old_y
                
                new_vertices[objName][i] = {}
                new_vertices[objName][i]["coords"] = [new_x, new_y, new_z]
                new_vertices[objName][i]["uv"] = self.get_uv(objName, i)
                new_vertices[objName][i]["norm"] = self.get_normals(objName, i)
        self._vertices = new_vertices
        
    def invert_faces(self):
        '''Invert faces. Swaps y and z vertices, along with their UV. 
        Assumes only triangle faces'''
        
        objs = self.get_objects()
        for obj in objs:
            faceCount = self.face_count(obj)
            for i in range(0, faceCount):
                self.swap_vertices(obj, i, "yz")
                
    def flip_UV(self, orient):
        '''Flips UV'''
        
        axis = 0 if orient == "u" else 1
        for obj in self._vertices.keys():
            verts = len(self._vertices[obj])
            for i in xrange(0, verts):
                value = self._vertices[obj][i]["uv"][axis]
                self._vertices[obj][i]["uv"][axis] = str(float(value) * -1)
        
    def triangulate_faces(self):
        '''Replaces face dictionary with a new dictionary where
        each face contains only 3 vertices.'''

        faceCount = 0
        meshes = self.get_objects()
        for obj in meshes:
            newFaces = []
            faces = self.get_faces(obj)
            for face in faces:
                
                verts = face.get_face_verts()
                if len(verts) > 3:
                    matNum = face.matNum
                    tri1 = verts[1:4]
                    tri2 = [verts[1], verts[3], verts[0]]
                    
                    face1 = Face(faceCount)
                    face.copy(face1)
                    face1.vertList = tri1
                    faceCount += 1
                    
                    face2 = Face(faceCount)
                    face.copy(face2)
                    face2.vertList = tri2
                    faceCount += 1
                    
                    newFaces.append(face1)
                    newFaces.append(face2)
                else:
                    newFaces.append(face)
                    faceCount += 1
            self._meshes[obj].faces = newFaces
            
    def separate_objects(self):
        '''Separates all meshes by material. Takes awhile'''
        
        new_meshNum = []
        numMat = len(self._materials)
        numMesh = len(self._meshes)
        if numMesh == 1:
            for obj in self._meshes:
                matDict = {}
                origVerts = self.get_vertices(obj)
                faceList = self.get_faces(obj)
                vertDict = {}
                for face in faceList:
                    matNum =  face.matNum
                    if not matDict.has_key(matNum):
                        new_meshNum.append(matNum)
                        vertDict[matNum] = {}
                        matDict[matNum] = {}
                        matDict[matNum]["verts"] = [] #vert
                        matDict[matNum]["faces"] = []
                        matDict[matNum]["count"] = 0 #for indexing
                        
                    vertList = face.vertList
                    new_vert_list = []
                    for vertNum in vertList:
                        #vertNum == old index
                        vertex = origVerts[vertNum]
                        vertNum = vertex.index
                        if not vertDict[matNum].has_key(vertNum):
                            matDict[matNum]["verts"].append(vertex)
                            new_vert_list.append(matDict[matNum]["count"])
                            vertDict[matNum][vertNum] = matDict[matNum]["count"]
                            matDict[matNum]["count"] += 1
                        else:
                            new_vert_list.append(vertDict[matNum][vertNum])
                    face.vertList = new_vert_list
    
                    matDict[matNum]["faces"].append(face)
        
            new_meshes = {}
            new_objList = []
            for i in xrange(len(new_meshNum)):
                matNum = new_meshNum[i]
                meshName = "obj[%d]" %matNum
                mesh = Mesh(meshName)
                mesh.vertices = matDict[matNum]["verts"]
                mesh.faces = matDict[matNum]["faces"]
                new_objList.append(meshName)
                new_meshes[meshName] = mesh
            
            self._meshes = new_meshes
            self._objList = new_objList
        
    def build_vertices(self):
        ''' '''
        print "build vertices"
        for obj in self._meshes:
        
            vertIndex = 0
            new_vertices = []
            dupeDict = {} #{hash : vert Index}
            
            faceList = self._meshes[obj].faces
            for face in faceList:
                new_vertList = []
                vertList = face.vertList
                uv = face.uv
                normals = face.normals
                for i in range(len(vertList)):
                    vertNum = vertList[i]
                    
                    vertUV = [0, 0]
                    if uv:
                        vertUV = [uv[i*2], uv[(i+1) * 2 - 1]]
                    coords = self._meshes[obj].vertices[vertNum].coords
                    group = tuple((vertNum, tuple(normals), tuple(vertUV)))
                    newHash = hash(group)
                    if not dupeDict.has_key(newHash):
                        dupeDict[newHash] = vertIndex
                        
                        vertex = Vertex(vertIndex)
                        vertex.coords = coords
                        vertex.add_normals(normals)
                        vertex.add_uv(vertUV)
                        new_vertList.append(vertIndex)
                        new_vertices.append(vertex)
                        vertIndex += 1
                    else:
                        newIndex = dupeDict[newHash]
                        new_vertList.append(newIndex)
                face.vertList = new_vertList
            self._meshes[obj].vertices = new_vertices
                    
            
    #Object functions
    
    def create_object(self, obj):
        
        self._objList.append(obj)
        self._meshes[obj] = Mesh(obj)
        
    def delete_object(self, objName):
        
        try:
            self._objList.remove(objName)
            self._vertices.pop(objName)
            self._faces.pop(objName)
        except:
            pass
        
    def rename_object(self, oldName, newName):
        
        #rename object list
        index = self._objList.index(oldName)
        self._objList[index] = newName
        
        #rename face dict
        
        face_dict = self._faces[oldName]
        self._faces.pop(oldName)
        self._faces[newName] = face_dict
        
        #rename vert dict
        vert_dict = self._vertices[oldName]
        self._vertices.pop(oldName)
        self._vertices[newName] = vert_dict
        
    def add_mesh_unknown(self, meshName, unk):
        
        self._meshes[meshName].add_unknown(unk)
        
    def get_mesh_unknown(self, meshName, index):
        
        return self._meshes[meshName].get_unknown(index)
        
    def add_mesh_transform(self, meshName, float16):
        
        matrix = Matrix4()
        matrix[:] = float16[:]
        self._meshes[meshName].set_transform(matrix)
        
    def get_mesh_transform(self, meshName):
        
        return self._meshes[meshName].get_transform()
    
    def transform_meshes(self):
        
        for mesh in self._meshes:
            if self._meshes[mesh].transform:
                
                matrix = Matrix4()
                matrix[:] = self._meshes[mesh].transform[:]
                new_vertices = []
                vertNum = self.vert_count(mesh)            
                for i in xrange(0, vertNum):
            
                    old_x, old_y, old_z = self.get_coords(mesh, i)
                    old_coords = Point3(old_x, old_y, old_z)
                    new_coords = matrix * old_coords
                    
                    vertex = self._meshes[mesh].vertices[i]
                    vertex.add_coords(new_coords)
                    new_vertices.append(vertex)
            else:
                new_vertices = self._meshes[mesh].vertices
                
            self._meshes[mesh].vertices = new_vertices
            
    def add_mesh_bone(self, meshName, boneIndex):
        
        self._meshes[meshName].add_bone(boneIndex)
        
    def get_mesh_bones(self, meshName):
        
        return self._meshes[meshName].get_bones()
    
    def get_mesh_name(self, meshNum):
        
        return self._objList[meshNum]
        
    def get_objects(self):
        
        return self._objList
    
    def mesh_count(self):
        
        return len(self._objList)
    
    def get_vertices(self, objName):
        
        return self._meshes[objName].vertices
    
    def vert_count(self, obj):
        '''Returns the number of vertices for the specified object'''
        
        return len(self._meshes[obj].vertices)
    
    def face_count(self, obj):
        '''Returns number of faces for the specific obj'''
        
        return len(self._meshes[obj].faces)
        
    #Vertex functions
    
    def create_vertex(self, obj, vertNum):
        '''Creates vertex. vertNum no longer used'''
        
        return self._meshes[obj].create_vertex()
    
    def add_vert_normals(self, obj, vertNum, norms):
        
        norms = map(float, norms)
        self._meshes[obj].vertices[vertNum].add_normals(norms)
    
    def add_coords(self, obj, vertNum, coords):
        
        coords = map(float, coords)
        self._meshes[obj].vertices[vertNum].add_coords(coords)
        self.update_max_min(coords)
        
    def add_vert_uv(self, obj,vertNum, uv):
        
        uv = map(float, uv)
        self._meshes[obj].vertices[vertNum].add_uv(uv)
        
    def add_vert_bones(self, obj, vertNum, bones):
        
        self._meshes[obj].vertices[vertNum].add_bone(bones)
        
    def get_vert_bones(self, obj, vertNum, num=4):
        
        bones = self._meshes[obj].vertices[vertNum].get_bones(num)
        for i in xrange(num - len(bones)):
            bones.append(0)
        return bones
    
    def add_vert_weights(self, obj, vertNum, weights):
        
        self._meshes[obj].vertices[vertNum].add_weight(weights)
        
    def get_vert_weights(self, obj, vertNum, num=3):
        
        weights = self._meshes[obj].vertices[vertNum].get_weights(num)
        for i in xrange(num - len(weights)):
            weights.append(0)
        return weights
    
    def get_coords(self, objName, vertNum):
        
        return self._meshes[objName].vertices[vertNum].coords
    
    def get_vert_normals(self, objName, vertNum):
        
        normals = self._meshes[objName].vertices[vertNum].normals
        if normals:
            return normals
        else:
            return [0, 0, 0]
        
    def get_vert_uv(self, objName, vertNum):
        
        try:
            return self._meshes[objName].vertices[vertNum].uv
        except:
            return [0, 0]
    
    def get_vertex(self, objName, vertNum):
        
        return self._meshes[objName].vertices[vertNum]
    
    #Face functions
    
    def create_face(self, obj, faceNum):
        
        self._meshes[obj].create_face()
        
    def add_face_material(self, obj, faceNum, matNum):
        
        self._meshes[obj].faces[faceNum].matNum = int(matNum)
    
    def add_face_verts(self, obj, faceNum, verts):

        verts = map(int, verts)
        self._meshes[obj].faces[faceNum].vertList = verts
        
    def add_face_uv(self, obj, faceNum, uv):
        '''uv is a list of floats'''
        
        uv = map(float, uv)
        self._meshes[obj].faces[faceNum].add_uv(uv)
    
    def add_face_normals(self, obj, faceNum, norms):
        
        norms = map(float, norms)
        self._meshes[obj].faces[faceNum].add_normals(norms)
        
    def get_face_uv(self, obj, faceNum):
        
        return self._meshes[obj].faces[faceNum].get_uv()
        
    def get_face_normals(self, obj, faceNum):
        
        return self._meshes[obj].faces[faceNum].get_normals()
        
    def swap_vertices(self, obj, faceNum, mode):
        
        axis1 = self.axis[mode[0]]
        axis2 = self.axis[mode[1]]
        
        verts = self._faces[obj][faceNum]["verts"]
        verts[axis1], verts[axis2] = verts[axis2], verts[axis1]
        
    def get_face(self, obj, faceNum):
        
        return self._meshes[obj].faces[faceNum]
    
    def get_face_num(self, faceNum):
        
        return self._meshes[obj].faces[faceNum].get_index()
        
    def get_faces(self, obj):
        
        return self._meshes[obj].faces
        
    def get_face_verts(self, obj, faceNum):
        
        return self._meshes[obj].faces[faceNum].vertList
    
    def get_face_mat(self, obj, faceNum):
        
        return self._meshes[obj].faces[faceNum].matNum
        
    #Material functions

    def create_material(self, matNum):
        
        self._matList.append(matNum)
        self._materials.append(Material(matNum))
    
    def add_material_name(self, matNum, matName):
        
        self._materials[matNum].add_name(matName)
        
    def add_texture_name(self, matNum, texName):
        
        self._materials[matNum].add_texture(texName)
        
        if texName not in self._texList:
            self.create_texture(texName)
        
    def add_rgba(self, matNum, rgba):
        
        rgba = map(float, rgba)
        self._materials[matNum].add_rgba(rgba)
    
    def add_diffuse(self, matNum, diffuse):
        
        diffuse = list(diffuse)
            
        if len(diffuse) == 1:
            diffuse.extend([diffuse[0], diffuse[0], 1])
        elif len(diffuse) == 3:
            diffuse.extend([1])
           
        diffuse = map(float, diffuse)
        self._materials[matNum].add_diffuse(diffuse)
        
    def add_ambient(self, matNum, ambient):
        
        ambient = list(ambient)
        if len(ambient) == 1:
            ambient.extend([ambient[0], ambient[0], 1])
        elif len(ambient) == 3:
            ambient.extend([1])
            
        ambient = map(float, ambient)
        self._materials[matNum].add_ambient(ambient)
        
    def add_power(self, matNum, power):
        
        self._materials[matNum].add_power(float(power))
    
    def add_emissive(self, matNum, emissive):
        
        emissive = list(emissive)
            
        if len(emissive) == 1:
            emissive.extend([emissive[0], emissive[0], 1])
        elif len(emissive) == 3:
            emissive.extend([1])
        
        emissive = map(float, emissive)
        self._materials[matNum].add_emissive(emissive)
    
    def add_specular(self, matNum, specular):
        
        specular = list(specular)
        
        if len(specular) == 1:
            specular.extend([specular[0], specular[0], 1])
        elif len(specular) == 3:
            specular.extend([1])
        
        specular = map(float, specular)
        self._materials[matNum].add_specular(specular)
        
    def get_material(self, matNum):
        
        try:
            matNum = self._materials[matNum]
        except:
            matNum = None
        return matNum
    
    def mat_count(self):
        
        return len(self._materials)
    
    def get_material_number(self, matName):
        
        return self._matList.index(matName)
    
    def get_material_name(self, matNum):
        
        return self._materials[matNum].get_name()
    
    def get_material_texture(self, matNum):
        
        return self._materials[matNum].get_texture()
    
    def get_rgba(self, matNum):
        
        return self._materials[matNum].get_rgba()
    
    def get_specular(self, matNum):
        
        return self._materials[matNum].get_specular()
        
    def get_ambient(self, matNum):
        
        return self._materials[matNum].get_ambient()
        
    def get_emissive(self, matNum):
        
        return self._materials[matNum].get_emissive()
        
    def get_diffuse(self, matNum):
        
        return self._materials[matNum].get_diffuse()
        
    def get_power(self, matNum):
        
        return self._materials[matNum].get_power()
    
    def add_material_unknown(self, matNum, unk):
        
        self._materials[matNum].add_unknown(unk)
        
    def get_material_unknown(self, matNum, index):
        
        try:
            return self._materials[matNum].get_unknown(index)
        except:
            return 0

    def get_materials(self):
        
        return self._matList
    
    #texture functions
    
    def create_texture(self, texName):
        
        if not texName in self._texList:
            self._texList.append(texName)
            self._textures[texName] = Texture(texName)
        
    def add_texture_checksum(self, texName, checksum):
        
        self._textures[texName].add_checksum(checksum)
        
    def get_texture_checksum(self, texName):
        
        return self._textures[texName].get_checksum()
    
    def add_texture_unknown(self, texName, unk):
        
        self._textures[texName].add_unknown(unk)
        
    def get_texture_unknown(self, texName, index):
        
        return self._textures[texName].get_unknown(index)
    
    def get_textures(self):
        
        return self._texList
     
    #frame functions
    
    def create_frame(self, index):
        
        self._frames.append(Frame(index))
        
    def add_frame_name(self, index, name):
        
        self._frames[index].add_name(name)
        self._frameDict[name] = index
        
    def add_frame_child(self, index, childID):
        
        self._frames[index].add_child(childID)
    
    def add_frame_parent(self, index, parentID):
        '''Adds parent frame ID'''
        
        self._frames[index].add_parent(parentID)
        
    def add_frame_mesh(self, index, meshName):
        
        self._frames[index].add_mesh(meshName)
        
    def add_frame_transform(self, index, float16):
        
        matrix = Matrix4()
        matrix[:] = float16[:]
        self._frames[index].add_transform(matrix)
        
    def get_frame_index(self, name):
        '''Return the index of the frame with the specified name'''
        
        return self._frameDict[name]
        
    def get_frame(self, index):
        '''Return the frame at the given index'''
        
        return self._frames[index]
    
    def get_frame_name(self, index):
        
        return self._frames[index].get_name()
    
    def get_frame_parent(self, index):
        
        return self._frames[index].get_parent()
    
    def get_frame_mesh(self, index):
        
        return self._frames[index].get_meshes()
    
    def get_frame_transform(self, index):
        
        return self._frames[index].get_transform()
    
    def get_frame_child_count(self, index):
        
        return self._frames[index].child_count()
    
    def get_frame_children(self, index):
        
        return self._frames[index].get_children()

    def get_frames(self):
        
        return self._frames
    
    def frame_count(self):
        
        return len(self._frames)
    
    def add_frame_bbox(self, index, bbox):
        
        self._frames[index].add_bbox(bbox)
        
    def get_frame_bbox(self, index, i):
        
        bbox = self._frames[index].get_bbox(i)
        if bbox:
            return bbox
        if i == 1:
            return self.max_x, self.max_y, self.max_z
        else:
            return self.min_x, self.min_y, self.min_z
    
    def add_frame_unknown(self, index, unk):
        
        self._frames[index].add_unknown(unk)
        
    def get_frame_unknown(self, index, i):
        
        return self._frames[index].get_unknown(i)
    
    #bone functions
    
    def create_bone(self, boneIndex):
        '''may store duplicate bone entries'''
        
        self._boneList.append(Bone(boneIndex))
    
    def add_bone_name(self, boneIndex, name):
        
        self._boneList[boneIndex].add_name(name)
        self._bones[name] = boneIndex
    
    def add_bone_transform(self, boneIndex, float16):
        
        matrix = Matrix4()
        matrix[:] = float16[:]
        self._boneList[boneIndex].add_transform(matrix)
    
    def add_bone_child(self, boneIndex, boneName):
        
        self._boneList[boneIndex].add_child(boneName)
    
    def add_bone_parent(self, boneIndex, index):
        
        self._boneList[boneIndex].add_parent(index)
    
    def get_bone_name(self, boneIndex):
        
        return self._boneList[boneIndex].get_name()
    
    def get_bone_transform(self, boneIndex):
        
        return self._boneList[boneIndex].get_transform()
    
    def get_bone_children(self, boneIndex):
        
        return self._boneList[boneIndex].get_children()
    
    def get_bone_parent(self, boneIndex):
        
        return self._boneList[boneIndex].get_parent()
        
    def get_bones(self):
        
        return self._boneList
    
    def bone_count(self):
        
        return len(self._boneList)
    
    #misc functions
    
    def skip_lines(self, string):
        
        line = self.inFile.readline()
        while not line.strip().startswith(string):
            line = self.inFile.readline()
        return line
    
    def get_header(self):
        
        return self.format3D
    
    def get_uv(self, objName, faceNum):
        '''Returns uv for the specified face. First checks if the face
        contains uv, then builds uv from the vertices'''
        
        face = self._meshes[objName].faces[faceNum]
        if face.uv:
            uv = face.uv
        else:
            uv = []
            for vert in face.vertList:
                uv.extend(self.get_vert_uv(objName, vert))
                
        return uv
    
class Vertex(object):
    '''A vertex object'''
    
    def __init__(self, index):
        
        self.uv = [0, 0]
        self.normals = [0, 0, 0]
        self.coords = [0, 0, 0]
        self.weights = [0, 0, 0, 0]
        self.bones = [0, 0, 0, 0]
        self.index = index
        
    def add_coords(self, coords):
        
        self.coords = coords
        
    def add_normals(self, normals):
        
        self.normals = normals
        
    def get_normals(self):
        
        return self.normals
        
    def add_uv(self, uv):
        
        self.uv = uv
        
    def get_uv(self):
        
        return self.uv
    
    def add_bone(self, bones):
        
        self.bones = bones
        
    def get_bones(self, num):
        
        return self.bones[:num]
    
    def add_weight(self, weights):
        
        self.weights = weights
        
    def get_weights(self, num):
        
        return self.weights[:num]
        
    def __repr__(self):
        
        return "<%s>" %str(self.index)
    
class Face(object):
    '''A face object'''
    
    def __init__(self, index):
    
        self.index = index
        self.vertList = []
        self.matNum = -1
        self.normals = []
        self.uv = []
        
    def add_face_verts(self, verts):
        
        self.vertList = verts
        
    def add_face_material(self, matNum):
        
        self.matNum = matNum
        
    def add_normals(self, normals):
    
        self.normals = normals
    
    def get_normals(self):
        
        return self.normals
    
    def add_uv(self, uv):
        
        self.uv = uv
    
    def get_uv(self):
        
        return self.uv
        
    def get_face_verts(self):
        
        return self.vertList
    
    def get_index(self):
        
        return self.index
    
    def copy(self, other):
        
        other.vertList = self.vertList[:]
        other.matNum = self.matNum
        other.normals = self.normals[:]
        other.uv = self.uv[:]
        
    def __repr__(self):
        
        return "<%s>" %str(self.index)
    
class Material(object):
    '''A material object'''
    
    def __init__(self, index):
        
        self.index = index
        self.name = ""
        self.rgba = []
        self.diffuse = [1, 1, 1, 1]
        self.ambient = [1, 1, 1, 1]
        self.specular = [0, 0, 0, 0]
        self.emissive = [0, 0, 0, 0]
        self.power = 1
        self.texName = ""
        self.unknowns = [] #illusion unknowns
        
    def add_name(self, name):
        
        self.name = name
        
    def get_name(self):
        
        return self.name
    
    def add_texture(self, texName):
        
        self.texName = texName
        
    def get_texture(self):
        
        return self.texName
    
    def add_rgba(self, rgba):
        
        self.rgba = rgba
        
    def get_rgba(self):
        
        return self.rgba
    
    def add_diffuse(self, diffuse):
        
        self.diffuse = diffuse
        
    def get_diffuse(self):
    
        return self.diffuse
    
    def add_ambient(self, ambient):
        
        self.ambient = ambient
        
    def get_ambient(self):
        
        return self.ambient
    
    def add_specular(self, specular):

        self.specular = specular
        
    def get_specular(self):
    
        return self.specular
    
    def add_emissive(self, emissive):
        
        self.emissive = emissive

    def get_emissive(self):
    
        return self.emissive
    
    def add_power(self, power):
        
        self.power = power
        
    def get_power(self):
    
        return self.power
              
    def add_unknown(self, unk):
        
        self.unknowns.append(unk)
        
    def get_unknown(self, index):
        
        if len(self.unknowns) >= index + 1:
            return self.unknowns[index]
        return 0
        
    def __repr__(self):
        
        return str(self.index)
    
class Texture(object):
    '''A texture object'''
    
    def __init__(self, texName):
        
        self.texName = texName
        self.height = -1
        self.width = -1
        self.checksum = 0
        self.unknowns = [] #illusion xx unknowns
        
    def add_dimensions(self):
        
        pass
    
    def get_dimensions(self):
        
        pass
    
    def add_checksum(self, checksum):
        
        self.checksum = checksum
        
    def get_checksum(self):
        
        return self.checksum
    
    def add_unknown(self, unk):
        
        self.unknowns.append(unk)
        
    def get_unknown(self, index):
        
        if len(self.unknowns) > 0:
            return self.unknowns[index]
        return 0
        
    def __repr__(self):
        
        return "<%s>" %self.texName
    
class Frame(object):
    '''A frame object'''
    
    def __init__(self, index):
        
        self.index = index
        self.name = ""
        self.children = [] #list of frame names
        self.meshes = [] #list of meshes contained
        self.transform = Matrix4() #4x4 matrix
        self.parent = -1 #index of parent frame
        self.bbox = []
        self.unknowns = []
        
    def add_name(self, name):
        
        self.name = name
        
    def get_name(self):
        
        return self.name
    
    def add_parent(self, parentIndex):
        
        self.parent = parentIndex
        
    def get_parent(self):
        
        return self.parent
        
    def add_transform(self, matrix):
        
        self.transform = matrix
        
    def get_transform(self):
        
        return self.transform[:]
    
    def add_child(self, frameName):
        
        self.children.append(frameName)
        
    def add_mesh(self, meshName):
        
        self.meshes.append(meshName)
        
    def get_meshes(self):
        '''Return the list of meshes'''
        
        return self.meshes
    
    def get_children(self):
        '''Return the list of child frames'''
        
        return self.children
        
    def child_count(self):
        '''Return the number of child frames'''
        
        return len(self.children)
    
    def add_bbox(self, bbox):
        
        self.bbox.append(bbox)
        
    def get_bbox(self, index):
        
        if len(self.bbox) > 0:
            return self.bbox[index]
        return []
    
    #xx specific
    def add_unknown(self, unk):
        
        self.unknowns.append(unk)
        
    def get_unknown(self, index):
        
        if len(self.unknowns) > 0:
            return self.unknowns[index]
        return 0
        
    def __repr__(self):
        
        return "<%s>" %self.name
    
class Bone(object):
    '''A bone object'''
    
    def __init__(self, index):
        
        self.index = index
        self.name = ""
        self.parent = -1
        self.children = []
        self.transform = Matrix4()
        
    def add_name(self, name):
        
        self.name = name
        
    def get_name(self):
        
        return self.name
    
    def add_parent(self, parent):
        '''Add parent bone index'''
        
        self.parent = parent
        
    def get_parent(self):
        
        return self.parent
    
    def add_child(self, boneName):
        
        self.children.append(boneName)
        
    def get_children(self):
        
        return self.children
    
    def add_transform(self, matrix):
        
        self.matrix = matrix
        
    def get_transform(self):
        
        return self.transform[:]
    
    def __repr__(self):
        
        return "<%s>" %self.name
    
class Mesh(object):
    '''A mesh object'''
    
    def __init__(self, name):
        
        self.name = name
        self.vertices = []
        self.faces = []
        self.dupeVerts = []
        self.transform = Matrix4()
        self.faceCount = 0
        self.vertCount = 0
        self.bones = []
        
        #flags
        self.faceUV = 0
        self.faceNorm = 0
        
        self.unknowns = [] #illusion xx unknowns
        
    def create_vertex(self):
        '''Add new Vertex to the list of vertices and return its index'''
        
        self.vertices.append(Vertex(self.vertCount))
        index = self.vertCount
        self.vertCount += 1
        return index
        
    def create_face(self):
        
        self.faces.append(Face(self.faceCount))
        self.faceCount += 1
        
    def set_transform(self, vector_16):
        '''Takes a 16-tuple vector and set the matrix'''
        
        self.transform[:] = vector_16[:]
        
    def get_transform(self):        
        '''Return a 16-float representation of the matrix'''
        
        return self.transform[:]
    
    def add_bone(self, boneIndex):
        
        self.bones.append(boneIndex)
        
    def get_bones(self):
        
        return self.bones
    
    def add_unknown(self, unk):
        
        self.unknowns.append(unk)
        
    def get_unknown(self, index):
        
        if len(self.unknowns) >= index + 1:
            return self.unknowns[index]
        return 0

    def __repr__(self):
        
        return "<%s>" %self.name
        